package com.miyake.dsp.otdr;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import com.miyake.dsp.otdr.ui.FiberNodeUI;

public class FiberNode {
	public enum NodeType {
		None,
		Connector,
		Splitter,
		ONU,
		OTDR,
		Open,
		APC,
		EDFA,
		Isolator, 
	}
	
	protected List<FiberNode> nodes = new ArrayList<FiberNode>();
	protected NodeProperties properties;
	protected NodeType nodeType;
	protected Object userObject;
	
	public FiberNode(NodeType nodeType, NodeProperties prop) {
		super();
		this.properties = prop;
		this.nodeType = nodeType;
		
		initialize();
	}

	private void initialize() {
//		nodes = new ArrayList<FiberNode>();
	}
	
	public void add(FiberNode node) {
		nodes.add(node);
	}
	
	public List<FiberNode> getNodes() {
		return nodes;
	}

	public void setNodes(List<FiberNode> nodes) {
		this.nodes = nodes;
	}

	public double[] getImpulseResponse(double incidentLevel, double resolution) {
		if (nodeType == NodeType.None || nodeType == NodeType.OTDR) {
			return this.getCumulativeSubTrace(incidentLevel, resolution);
		}
		else {			
			double ior = properties.getFiberProp().getIor();
			int size = (int) (properties.getLength()*1000.0 / resolution * ior);
			double[] thisTrace = new double[size];
			double level = incidentLevel;
			for (int i = 0; i < size; i++) {
				level = incidentLevel * Math.pow( properties.getFiberProp().getLinAttenuation(), 
						i*resolution * ior);
				thisTrace[i] = properties.getFiberProp().getLinBsc() * level;
			}
			
			thisTrace[size-1] += level * properties.getLinReflectance();
			
			if (nodes.size() == 0) {
				return thisTrace;
			}
			else {
				double loss = properties.getLinLoss() * 1.0 / (double)nodes.size();
				//loss = Math.pow(loss, 2.0); // double loss
				double endLevel = level * loss;
				double[] subTrace = this.getCumulativeSubTrace(endLevel, resolution);
				double[] ret = new double[thisTrace.length + subTrace.length];
				for (int i = 0; i < ret.length; i++) {
					if (i < thisTrace.length) {
						ret[i] = thisTrace[i];
					}
					else {
						ret[i] = subTrace[i - thisTrace.length];
					}
				}
				return ret;
			}		
		}
	}

	protected double[] getCumulativeSubTrace(double incidentLevel, double resolution) {
		Vector<double[]> subTraces = new Vector<double[]>();
		
		int maxLength = 0;
		for (FiberNode node : nodes) {
			double[] tmpTrace = node.getImpulseResponse(incidentLevel, resolution);
			subTraces.add(tmpTrace);
			
			if (maxLength < tmpTrace.length) {
				maxLength = tmpTrace.length;
			}
		}
		
		double subTrace[] = new double[maxLength];
		for (int i = 0; i < maxLength; i++) {
			for (int j = 0; j < subTraces.size(); j++) {
				double[] d = subTraces.elementAt(j);
				if (i < d.length) {
					subTrace[i] += d[i];//*d[i];
				}
			}
			//subTrace[i] = Math.sqrt(subTrace[i]);
		}		
		
		return subTrace;
	}

	public NodeProperties getProperties() {
		return properties;
	}

	public void setProperties(NodeProperties properties) {
		this.properties = properties;
	}

	public NodeType getNodeType() {
		return nodeType;
	}

	public void setNodeType(NodeType nodeType) {
		this.nodeType = nodeType;
	}

	public void setWavelength(double wavelength) {
		this.properties.setWavelength(wavelength);

		for (FiberNode node : this.nodes) {
			node.setWavelength(wavelength);
		}
	}

	public Object getUserObject() {
		return userObject;
	}

	public void setUserObject(Object userObject) {
		this.userObject = userObject;
	}
	
	public int getFiberCount() {
		int ret = 0;
		for (FiberNode c : nodes) {
			ret += c.getFiberCount();
		}

		return ret;
	}
	
	public void updateFiber(FiberManager fiber) {
		properties.setFiber(fiber.getFiber(properties.getFiber().getName()));
		for (FiberNode c : nodes) {
			c.updateFiber(fiber);
		}		
	}
}
